/***************************************************************************
 * WEBFOLDER.CPP
 *
 *  OWebFolder: Implementation for COfficeWebFolder (list view helper).
 *
 *  Copyright (c)1999-2003; Microsoft Corporation, All rights reserved.
 *  Written by DSOI Office Integration, Microsoft Developer Support
 *
 *  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
 *  EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED
 *  WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
 *
 ***************************************************************************/
#include "owebfolder.h"


///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder
//
//  In the sample, the COfficeWebFolder is a wrapper around the list view for a 
//  particular CMSDAIPPRowset object passed in during intialization. That is to say,
//  this class handles all direct deallings with the CMSDAIPPRowset object it holds
//  and supplies the list view with information when asked.
//
//  Keep in mind, there can be several COfficeWebFolder objects alive at one time, and
//  for the sake of performance and managability, they all share a single list view. So
//  the COfficeWebFolder object is called upon to handle correct updates when its view
//  should be active.
//
COfficeWebFolder::COfficeWebFolder(COfficeWebFolder* pparent, CMSDAIPPRowset* prowset, HWND hwnd)
{
    ODS("New COfficeWebFolder created.\n");
	m_cRef           = 1;
	m_pParentFolder  = pparent;
	m_pIPPRowset     = prowset;
	m_hwndList       = hwnd;

	ADDREF_INTERFACE(m_pParentFolder);
	ADDREF_INTERFACE(m_pIPPRowset);

    m_pszFolderName = ((m_pIPPRowset) ? DsoConvertToMBCS(m_pIPPRowset->GetFolderURL()) : NULL);
}

COfficeWebFolder::~COfficeWebFolder(void)
{
    ODS("COfficeWebFolder destroyed.\n");
	RELEASE_INTERFACE(m_pIPPRowset);
	RELEASE_INTERFACE(m_pParentFolder);
    FREE_MEMPOINTER(m_pszFolderName);
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::CreateWebFolderView (static)
//
//  Static creation function. We must be handed a CMSDAIPPRowset object that this
//  object will present as a view, and the HWND to the parent's list view. If this 
//  is a sub folder, a reference to the parent folder is also kept.
//  
STDMETHODIMP_(COfficeWebFolder*) COfficeWebFolder::CreateWebFolderView(COfficeWebFolder* pParent, CMSDAIPPRowset* pFolderRowset, HWND hwndList)
{
    ODS("Called COfficeWebFolder::\n");
	ASSERT(pFolderRowset); CHECK_NULL_RETURN(pFolderRowset, NULL);
	COfficeWebFolder* prs = new COfficeWebFolder(pParent, pFolderRowset, hwndList);
	return prs;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder IUnknown Implementation
//
//  Here mostly for ref counting. In later editions, we might also want to handle
//  OLE Drag Drop (IDropTarget/IDropSource).
//  
STDMETHODIMP COfficeWebFolder::QueryInterface(REFIID riid, void ** ppv)
{
	HRESULT hr = S_OK;
	CHECK_NULL_RETURN(ppv, E_POINTER); *ppv = NULL;

	if ((IID_IUnknown == riid))
	{
		*ppv = (IUnknown*)this;
	}
	else hr = E_NOINTERFACE;

	if (*ppv) AddRef();

	return hr;
}

STDMETHODIMP_(ULONG) COfficeWebFolder::Release(void)
{
	if (--m_cRef > 0) return m_cRef;

    ODS("COfficeWebFolder in final release\n");
	m_hwndList = NULL;
	delete this;
	return 0;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder Public Functions
//
///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::PopulateListView
//
//  The most called function in this class. PopulateListView simply iterates the 
//  items in CMSDAIPPRowset and pushes them into the list view. Each items 
//  LPMSDAIPP_ROWSETINFO data is stored in the list view as well (for fast access).
//
STDMETHODIMP COfficeWebFolder::PopulateListView()
{
	HRESULT       hr;
	ULONG         cItems, cFolders;
	LVITEM        lvitem;
	LPMSDAIPP_ROWSETINFO  pRowItem = NULL;

    ODS("COfficeWebFolder::PopulateListView\n");
	ASSERT(m_pIPPRowset); CHECK_NULL_RETURN(m_pIPPRowset, E_UNEXPECTED);

 // Clean out the existing view (if any). We don't want to force a full
 // refresh in this case since this is often called just after getting
 // a new rowset...
	hr = CleanupListView(FALSE);
	LEAVE_ON_ERROR(hr, "CleanupListView failed");

 // Set up the LVITEM to insert into the list view...
	memset(&lvitem, 0, sizeof(LV_ITEM));
	lvitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
	cItems = (cFolders = 0);

	while (TRUE)
	{
	 // For every item, create a new MSDAIPP_ROWSETINFO struct to hold
	 // the data for this file/folder...
		pRowItem = new MSDAIPP_ROWSETINFO;
		if (pRowItem == NULL) LEAVE2(hr, E_OUTOFMEMORY, "Out of memory for MSDAIPP_ROWSETINFO");

	 // Ask the rowset for the data...
		hr = m_pIPPRowset->GetNextFolderItem(pRowItem);
		LEAVE_ON_ERROR(hr, "IPPRowset->GetNextFolderItem failed");

	 // If this returns S_FALSE, we hit the end of the rowset, so we 
	 // can stop. We'll set hr = S_OK since this expected...
		if (hr != S_OK) { hr = S_OK; break; } 

	 // If we do have an item, add it to the list view...
		lvitem.iItem   = cItems++;
		lvitem.pszText = DsoConvertToMBCS(pRowItem->wszDisplayName);
		lvitem.iImage  = (pRowItem->bIsSubFolder ? 1 : 0);
		lvitem.lParam  = (LPARAM)pRowItem;
		ListView_InsertItem(m_hwndList, &lvitem);

	 // Set pRowItem = NULL (since the listview cached it)... 
		pRowItem = NULL;
	}

 // Sort all the items based on our sort procedure...
	ListView_SortItems(m_hwndList, ListSortProc, 0);

leave_routine:
	FREE_MEMPOINTER(pRowItem);
	return hr;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::CleanupListView
//
//  We remove all the items from the view, and free any MSDAIPP_ROWSETINFO data 
//  cached from the last population. If fFullRefresh is TRUE, we will also release
//  the rowset and force a reload (to cause a full resynch with the server).
//
STDMETHODIMP COfficeWebFolder::CleanupListView(BOOL fFullRefresh)
{
	UINT cItems, idx;
	LVITEM lvitem;
    TRACE1("COfficeWebFolder::CleanupListView(%d)\n", fFullRefresh);

 // Reset the index on the CMSDAIPPRowset object...
	if (m_pIPPRowset)
    {
		m_pIPPRowset->ResetFolderList();

	  // Attempt a full refresh if requested. If this fails it is
	  // slightly bad news, but we keep the old rowset just in case...
        if ((fFullRefresh) && (FAILED(m_pIPPRowset->ReloadRowset())))
            return E_FAIL;
    }

 // Go through the listview and clean up the lParam data we stored...
	if ((m_hwndList) && (cItems = ListView_GetItemCount(m_hwndList)))
	{
		memset(&lvitem, 0, sizeof(LVITEM));
		for (idx = 0; idx < cItems; idx++)
		{
			lvitem.iItem = idx;
			lvitem.mask = LVIF_PARAM;
			if (ListView_GetItem(m_hwndList, &lvitem) && (lvitem.lParam))
			{
				delete (LPMSDAIPP_ROWSETINFO)(lvitem.lParam);
			}
		}

	 // Delete all the items from the view...
		ListView_DeleteAllItems(m_hwndList);
	}

	return S_OK;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::CreateNewSubFolder
//
//  Asks the rowset to make a new collection (subfolder) by the name given. If this 
//  succeeds, we get the MSDAIPP_ROWSETINFO for the new folder and add it to the 
//  active list view.
//
STDMETHODIMP COfficeWebFolder::CreateNewSubFolder(LPCWSTR pwszNewFolder)
{
	HRESULT hr = E_FAIL;
	MSDAIPP_ROWSETINFO *pRowItem = NULL;
	LVITEM  lvitem;

    TRACE1("COfficeWebFolder::CreateNewSubFolder(%S)\n", pwszNewFolder);
	ASSERT(m_pIPPRowset); CHECK_NULL_RETURN(m_pIPPRowset, E_UNEXPECTED);

	pRowItem = new MSDAIPP_ROWSETINFO;
	if (pRowItem == NULL) LEAVE2(hr, E_OUTOFMEMORY, "Out of memory for MSDAIPP_ROWSETINFO");

	hr = m_pIPPRowset->AddNewCollection(pwszNewFolder, pRowItem);
	LEAVE_ON_ERROR(hr, "IPPRowset->AddNewCollection failed");

	memset(&lvitem, 0, sizeof(LV_ITEM));
	lvitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
	lvitem.iItem = ListView_GetItemCount(m_hwndList);
	lvitem.pszText = DsoConvertToMBCS(pRowItem->wszDisplayName);
	lvitem.iImage  = 1;
	lvitem.lParam  = (LPARAM)pRowItem;

	if (ListView_InsertItem(m_hwndList, &lvitem) != -1)
		pRowItem = NULL;

leave_routine:
	FREE_MEMPOINTER(pRowItem);
	return hr;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::DownloadFile
//
//  Based on the index, we preform a download to the location given. All the real
//  work is done in the CMSDAIPPRowset object.
//
STDMETHODIMP COfficeWebFolder::DownloadFile(UINT idx, LPCWSTR pwszFolder)
{
	HRESULT hr = E_FAIL;
	MSDAIPP_ROWSETINFO *pitem;

    TRACE2("COfficeWebFolder::DownloadFile(%d, %S)\n", idx, pwszFolder);
	ASSERT(m_pIPPRowset); CHECK_NULL_RETURN(m_pIPPRowset, E_UNEXPECTED);

	pitem = GetRowsetInfo(idx);
	if (pitem)
	{
		hr = m_pIPPRowset->DownloadFile(pitem, pwszFolder);
	}
	return hr;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::UploadFile
//
//  Adds the given file to the rowset and then appends the item to the list view if
//  the upload is successful. Most of the work is done in the CMSDAIPPRowset object.
//
STDMETHODIMP COfficeWebFolder::UploadFile(LPCWSTR pwszFileToUpload)
{
	HRESULT hr = E_FAIL;
	MSDAIPP_ROWSETINFO *pitem = NULL;
	LVITEM  lvitem;

    TRACE1("COfficeWebFolder::UploadFile(%S)\n", pwszFileToUpload);
	ASSERT(m_pIPPRowset); CHECK_NULL_RETURN(m_pIPPRowset, E_UNEXPECTED);

    hr = m_pIPPRowset->UploadFile(pwszFileToUpload, &pitem);
    if (SUCCEEDED(hr) && (pitem))
    {
	    memset(&lvitem, 0, sizeof(LV_ITEM));
		lvitem.iItem = ListView_GetItemCount(m_hwndList);
	    lvitem.mask = LVIF_TEXT | LVIF_IMAGE | LVIF_PARAM;
	    lvitem.pszText = DsoConvertToMBCS(pitem->wszDisplayName);
	    lvitem.iImage  = 0;
	    lvitem.lParam  = (LPARAM)pitem;
	    ListView_InsertItem(m_hwndList, &lvitem);
    }
	return hr;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::DeleteItem
//
//  Removes the item from both the rowset and the list view.
//
STDMETHODIMP COfficeWebFolder::DeleteItem(UINT idx)
{
	HRESULT hr = E_FAIL;
	MSDAIPP_ROWSETINFO *pitem;
	LVITEM lvitem;

    TRACE1("COfficeWebFolder::DeleteItem(%d)\n", idx);
	ASSERT(m_pIPPRowset); CHECK_NULL_RETURN(m_pIPPRowset, E_UNEXPECTED);

	pitem = GetRowsetInfo(idx);
	if (pitem)
	{
		hr = m_pIPPRowset->DeleteURLResource(pitem->wszURL);
		if (SUCCEEDED(hr))
		{
			memset(&lvitem, 0, sizeof(LVITEM));
			lvitem.iItem = idx;
			lvitem.mask = LVIF_PARAM;
			if (ListView_GetItem(m_hwndList, &lvitem) && (lvitem.lParam))
			{
				delete (LPMSDAIPP_ROWSETINFO)(lvitem.lParam);
			}
			ListView_DeleteItem(m_hwndList, idx);
		}
	}
	return hr;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::GetSelectedItemIndex
//
//  Gets the currently selected item in the list view.
//
STDMETHODIMP_(INT) COfficeWebFolder::GetSelectedItemIndex()
{
    ODS("COfficeWebFolder::GetSelectedItemIndex\n");
	return ListView_GetNextItem(m_hwndList, -1, LVNI_FOCUSED|LVNI_SELECTED);
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::GetSelectedItemIndex
//
//  Gets the LPMSDAIPP_ROWSETINFO data for the item given.
//
STDMETHODIMP_(LPMSDAIPP_ROWSETINFO) COfficeWebFolder::GetRowsetInfo(UINT idx)
{
	LVITEM lvitem;
    ODS("COfficeWebFolder::GetRowsetInfo\n");

	memset(&lvitem, 0, sizeof(LVITEM));
	lvitem.iItem = idx;	lvitem.mask = LVIF_PARAM;
	if (ListView_GetItem(m_hwndList, &lvitem) && (lvitem.lParam))
		return (LPMSDAIPP_ROWSETINFO)(lvitem.lParam);

	return NULL;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::IsItemSubFolder
//
//  Quick check if the item is a subfolder.
//
STDMETHODIMP_(BOOL) COfficeWebFolder::IsItemSubFolder(UINT idx)
{
	BOOL fIsFolder = FALSE;
	MSDAIPP_ROWSETINFO *pRowItem = GetRowsetInfo(idx);
	if (pRowItem)
	{
		fIsFolder = (pRowItem->bIsSubFolder != 0);
	}
	return fIsFolder;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::SetDisplayInfo
//
//  Gets display data for a Report view. (Called by the list view itself.)
//
STDMETHODIMP_(BOOL) COfficeWebFolder::SetDisplayInfo(NMLVDISPINFO *plvdi)
{
	MSDAIPP_ROWSETINFO *pRowItem = (LPMSDAIPP_ROWSETINFO)(plvdi->item.lParam);
	LPSTR pszTemp = NULL;

// We should be given the data for the item the listview wants more info for...
	if (pRowItem)
	{
	 // Based on what it wants, coerce the data accordingly...
		switch (plvdi->item.iSubItem)
		{
		// It wants the display name...
		case 0: pszTemp = DsoConvertToMBCS(pRowItem->wszDisplayName); break;

		// It wants the size of the file (we don't give size for a folder)...
		case 1:	if (pRowItem->bIsSubFolder == FALSE)
				pszTemp = GetFormattedSize(pRowItem->ddwStreamSize);  break;

		// It wants the file type...
		case 2:	if (pRowItem->bIsSubFolder == FALSE)
				pszTemp = DsoConvertToMBCS(pRowItem->wszContentType);  break;

		// It wants the last modified date...
		case 3: pszTemp = GetFormattedDate(pRowItem->ftLastModified); break;
		}

	 // Copy the data into the buffer provided...
		if (pszTemp) 
			lstrcpy(plvdi->item.pszText, pszTemp);
	}

	FREE_MEMPOINTER(pszTemp);
	return TRUE;
}

///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::OpenSubFolder
//
//  From the current rowset, ask it to make a new rowset based on the folder name
//  given by the index item. If this is successful, we wrap the new rowset into 
//  another COfficeWebFolder object that is then passed back the host. That object
//  will become the current view and fill the listview with its data...
//
STDMETHODIMP COfficeWebFolder::OpenSubFolder(UINT idx, COfficeWebFolder** ppNewFolder)
{
	HRESULT             hr             = E_FAIL;
	MSDAIPP_ROWSETINFO *pRowItem       = NULL;
	CMSDAIPPRowset     *pMsdaippRowset = NULL;
	COfficeWebFolder   *pNewFolder     = NULL;

    ODS("COfficeWebFolder::OpenSubFolder\n");
	ASSERT(ppNewFolder);  CHECK_NULL_RETURN(ppNewFolder,  E_POINTER);
	ASSERT(m_pIPPRowset); CHECK_NULL_RETURN(m_pIPPRowset, E_UNEXPECTED);
	*ppNewFolder = NULL;

 // Get the item choosen and verify it is a subfolder...
	pRowItem = GetRowsetInfo(idx);
	if ((pRowItem) && (pRowItem->bIsSubFolder))
	{
	 // Ask the rowset to open the item as another rowset...
		hr = m_pIPPRowset->OpenRowset(pRowItem->wszURL, &pMsdaippRowset);
		LEAVE_ON_ERROR(hr, "IPPRowset->OpenRowset failed");

	 // Make the view, using this as the parent and the new rowset as the
	 // object to encapsulate....
		pNewFolder = COfficeWebFolder::CreateWebFolderView(this, pMsdaippRowset, m_hwndList);
		if (pNewFolder == NULL) LEAVE2(hr, E_OUTOFMEMORY, "OOM on CreateWebFolderView");

	 // We hand back the folder, so AddRef here (to keep alive on leave)...
		(*ppNewFolder = pNewFolder)->AddRef();
	}

leave_routine:
	RELEASE_INTERFACE(pMsdaippRowset);
	RELEASE_INTERFACE(pNewFolder);
	return hr;
}


///////////////////////////////////////////////////////////////////////////////////
// COfficeWebFolder::ListSortProc (static)
//
//  Sort routine for getting the list items to appear like Explorer.
//
INT CALLBACK COfficeWebFolder::ListSortProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
{
	LPMSDAIPP_ROWSETINFO pItem1 = (LPMSDAIPP_ROWSETINFO)lParam1;
	LPMSDAIPP_ROWSETINFO pItem2 = (LPMSDAIPP_ROWSETINFO)lParam2;
	UINT ui;

	if ((pItem1->bIsSubFolder) && !(pItem2->bIsSubFolder))
		return -1;
	
	if ((pItem2->bIsSubFolder) && !(pItem1->bIsSubFolder))
		return 1;

	ui = DsoCompareStrings(pItem1->wszDisplayName, pItem2->wszDisplayName);
	return (INT)(ui - CSTR_EQUAL);
}